package main

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"time"
)

func test(arr []int) {
	arr[0] = 11
}

// 字符串切片处理
func stringSlice() {
	str := "hello"
	str2 := str[:3]
	//str2: hel
	fmt.Println("str2:", str2)
}
func randArr() {
	var arr [3]int
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < len(arr); i++ {
		arr[i] = rand.Intn(100)
	}
	fmt.Println("原数组:", arr)

}
func arr() {
	//1.先声明数组，再赋值
	var arr1 [2]string
	var arrInt [2]int
	fmt.Println("未赋值数据的初始数组:", arrInt)
	arr1[0] = "1"
	arr1[1] = "2"
	//无法自动扩容，只能放指定容量数据0
	//arr1[2]=3
	fmt.Println(arr1)
	for index, val := range arr1 {
		fmt.Println(strconv.Itoa(index) + "->" + val)
	}
	//2.执行初始化一个已知变量数组，[N]必须指定长度为 N，则数组总长度为 N，{}内元素个数不能超过 N
	arr2 := [6]string{"h", "e", "l", "l", "o"}
	for i := 0; i < len(arr2); i++ {
		fmt.Printf(arr2[i])
	}
	fmt.Println()
	//3. 指定下标
	arr3 := [5]string{1: "e", 0: "h", 4: "o", 3: "l", 2: "l"}
	fmt.Println("arr3:", arr3)
	//4.[...] 方式创建数组
	arr4 := [...]int{1, 2, 3, 4, 5}
	fmt.Println("arr4,", arr4)
	//5.new
	newIntArr := new([3]int)
	//newIntArr: &[0,0,0]
	fmt.Printf("newIntArr 地址:%p\n", newIntArr)
	newIntArr[0] = 1
	//&[1 0 0]
	fmt.Println(newIntArr)
	//[1 0 0]
	fmt.Println(*newIntArr)
	//arr1的地址0xc0000a6020
	fmt.Printf("arr1的地址%p\n", &arr1)
	//arr1[0]的地址0xc0000a6020 （16进制）
	fmt.Printf("arr1[0]的地址%p\n", &arr1[0])
	//arr1[1]的地址0xc0000a6030
	fmt.Printf("arr1[1]的地址%p\n", &arr1[1])
}

/**
 	切片数据结构变量：
	1.指向原生数组的指针
    2.数组切片中元素个数
	3.切片已分配的存储空间
*/
func sliceTest() {
	// 创建一个数组
	arr := [10]int{1, 2, 3}
	//1. 通过数组创建切片 TODO 支持[start:end]方式访问数组、切片和字符串
	var mySlice []int = arr[:3]
	//[1 2 3]
	fmt.Println(mySlice)
	mySlice[0] = 2
	fmt.Println(arr)
	//2.直接创建
	var slice2 = []int{12, 3}
	//[12 3]
	fmt.Println(slice2)
	/*
		3.使用make(数据类型，长度)创建:make为内置函数，第一个参数执行数据类型，第二个参数指定长度length，这样就创建了一个长度为2的切片（可理解为数组）默认为对应类型的零值，第3个参数为分配的存储空间cap
	*/
	slice := make([]string, 4, 12)
	slice[0] = "!"
	slice[1] = "2"
	slice[2] = "3"
	slice[3] = "4"
	//大于指定长度时，使用append函数添加，会自动扩容【当然，如果一开始已经分配了足够的空间就可以直接用了】,第二个参数为不定参数
	slice = append(slice, "5")
	fmt.Println("扩容后的", slice)
	// slice的容量 12
	fmt.Println(cap(slice))
	// 实际的元素个数 5
	fmt.Println(len(slice))

	// 删除切片索引为2开始的2个数据 append(slice[:start],slice[num+start:]...)
	// start开始删除的索引，num:从start开始删除几个
	slice = append(slice[:3], slice[2+3:]...)
	for i, v := range slice {
		fmt.Println("i->v:" + strconv.Itoa(i) + "->" + v)
	}

}

/**
复制切片数据
*/
func copyTest() {
	var slice = make([]int, 2)
	var target = make([]int, 3)
	target[0] = 1
	target[1] = 2
	target[2] = 3

	// copy后会拷贝一个副本，因此修改原切片不会影响目标切片
	copy(slice, target)
	//未赋值的位置，默认为零值
	//[1,2,0]
	fmt.Println(slice)
}

/**
映射（java的map）
*/
func mapCreate() {
	//1.声明一个 map，不可直接使用，因为没有分配内存
	var map0 map[string]int
	map0 = make(map[string]int)
	map0["a"] = 1
	fmt.Println("map0,", map0)
	//2.创建一个映射 make(map[key类型]value类型,cap)，cap 为初始大小
	var map1 = make(map[string]int, 10)
	// 赋值
	map1["a"] = 1
	map1["b"] = 2
	map1["c"] = 3

	//map[a:1 b:2]
	fmt.Println(map1)
	//1
	fmt.Println(map1["a"])

	//删除映射中元素 delete(mapName,keyName)
	delete(map1, "a")
	//map[b:2]
	fmt.Println(map1)

	//2.初始元素创建 map
	var map2 = map[string]int{
		"1": 1,
		"2": 2,
	}
	//map[1:1 2:2]
	fmt.Println(map2)
}

func mapFor() {

	var map1 = make(map[string]int, 10)
	map1["a"] = 1
	map1["b"] = 2
	map1["c"] = 3

	fmt.Println("range with key")
	for key := range map1 {
		fmt.Println(key + "->" + strconv.Itoa(map1[key]))
	}
	fmt.Println("range with key val")
	for key, val := range map1 {
		fmt.Println(key + "->" + strconv.Itoa(val))
	}
	//没有对应 key ，返回 int 零值：0
	fmt.Println(map1["d"])
	val, res := map1["D"]
	//0,对应 key 的值
	fmt.Println(val)
	//是否存在对应的 key，这里返回 false
	fmt.Println(res)
}

func mapSort() {

	map1 := make(map[string]string)
	map1["b"] = "1"
	map1["c"] = "2"
	map1["a"] = "3"
	map1["f"] = "4"
	map1["e"] = "5"

	keys := make([]string, 0)
	for key := range map1 {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	fmt.Println(keys)
	for i := 0; i < len(keys); i++ {
		fmt.Println(keys[i] + "->" + map1[keys[i]])
	}
}

func sliceCut() {
	slice := []int{1, 2, 3, 4, 5}
	// 截取后，虽然两个切片地址不同，但是其底层数组相同
	// slice2依然指向原始切片slice的底层数组，因此修改 slice2也会修改 slice 对应的原始位置
	slice2 := slice[2:4]
	//2
	fmt.Println(len(slice2))
	fmt.Println(cap(slice2)) //3
	//slice2[0]=10
	fmt.Println(slice2[0]) //10
	fmt.Println(slice[2])  //10

	fmt.Printf("%p\n", slice)  //0xc000018120
	fmt.Printf("%p\n", slice2) //0xc000018130

	s := make([]int, 3)
	s[0] = 1
	s[1] = 2
	s[2] = 3
	// 切片的赋值也是值复制，但是这里的值复制说的是运行时的切片结构复制，而切片底层的数组依然使用的是同一样，即两个切片地址不同，但是切片中指向的数组的地址相同
	//即 s1与 s 共用一个数组的内存空间，因此 s1修改，s 也会改变
	s1 := s
	s1[0] = 2
	fmt.Println(s[0])      //2
	fmt.Printf("%p\n", s1) //0xc0000160e0
	fmt.Printf("%p\n", s)  //0xc0000160e0

	//如果希望不影响原数组，则使用 copy
	s2 := make([]int, 4)
	copy(s2, s)
	fmt.Println(s2) //2 2 3 0
}

func sliceDelete() {
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	//删除第一个
	slice = slice[1:]
	fmt.Println(slice) //2,3,,,,9

	//删除最后一个
	slice = slice[:len(slice)-1]
	fmt.Println(slice) //2,3,,,,8

	//删除第3个
	slice = append(slice[:3], slice[4:]...)
	fmt.Println(slice)
}

func map2() {
	var m map[int]int
	// 可以对未初始化的 map 取值，默认为零值
	fmt.Println(m[0])
	// 但不能赋值 ，会 panic
	m[0] = 1

}

func main() {
	//slice := []int{1, 2, 3}
	//test(slice)
	//fmt.Println(slice)
	//sliceTest()
	//stringSlice()
	//copyTest()
	//arr()
	//arr:=[2]int{1,2}
	//test(arr)
	//mapCreate()
	//mapSort()

	map2()
}
